package com.eastfair.notice.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.eastfair.boot.service.SuperServiceImpl;
import com.eastfair.cache.redis.RedisOps;
import com.eastfair.core.base.R;
import com.eastfair.enums.RedisKeyEnum;
import com.eastfair.notice.dao.ImDialogMapper;
import com.eastfair.notice.entity.ImDialog;
import com.eastfair.notice.service.ImDialogService;
import com.eastfair.projectcore.api.ProjectCoreServiceFeign;
import com.eastfair.core.snowflake.DefaultUidGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 * 业务实现类
 * Im对话列表
 * </p>
 *
 * @author liuxiaobai
 * @date 2021-08-12
 */
@Slf4j
@Service
public class ImDialogServiceImpl extends SuperServiceImpl<ImDialogMapper, ImDialog> implements ImDialogService {

    private final Lock lock = new ReentrantLock();

    @Autowired
    ProjectCoreServiceFeign projectCoreServiceFeign;
    @Autowired
    private RedisOps redisOps;

    /**************sharding 分库分表操作 *****************/
    @Override
    public boolean updateByIdOfShard(ImDialog entity) {
        UpdateWrapper<ImDialog> updateWrapper = new UpdateWrapper<>();
        //可将指定字段更新为null
        updateWrapper.lambda().eq(ImDialog::getProjectId, entity.getProjectId()).eq(ImDialog::getId, entity.getId());
        entity.setProjectId(null);
        return SqlHelper.retBool(this.getBaseMapper().update(entity, updateWrapper));
    }

    @Override
    protected R<Boolean> handlerSave(ImDialog model) {
        //雪花ID
        model.setId(DefaultUidGenerator.getUidOfProjectId(model.getProjectId()));
        return R.successDef();
    }

    @Override
    public String saveDialog(Long projectId, Long fromUserId, Long toUserId) {
        //根据传入fromUserId和toUserId生成唯一code
        String uniqueCode = getUniqueCode(fromUserId, toUserId);
        //code插入project对应对话set
        Long successNum = redisOps.getSetOps().add(String.format(RedisKeyEnum.IM_DIALOG.getKey(), projectId), uniqueCode);
        if (successNum == null || successNum == 0) {
            //code已存在 不再入库 直接返回
            return uniqueCode;
        }

        lock.lock();
        try {
            //校验库中uniqueCode是否已存在 防止缓存穿透
            if (checkCodeExist(projectId, uniqueCode)) {
                return uniqueCode;
            }
            //插入成功 表示首次对话 记录入库
            save(ImDialog.builder()
                    .projectId(projectId)
                    .fromUserId(fromUserId)
                    .toUserId(toUserId)
                    .uniqueCode(uniqueCode)
                    .build());
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        } finally {
            lock.unlock();
        }

        return uniqueCode;
    }

    @Override
    public String getUniqueCode(Long fromUserId, Long toUserId) {
        if (fromUserId == null || toUserId == null) {
            return "";
        }
        //对话双方userId从小到大排序后拼接作为对话唯一code
        return String.valueOf(Long.min(fromUserId, toUserId)) + Long.max(fromUserId, toUserId);
    }

    /**
     * 校验库中uniqueCode是否已存在
     *
     * @param projectId 项目ID
     * @param code      对话唯一code
     * @return 存在-true 不存在-false
     */
    private boolean checkCodeExist(Long projectId, String code) {
        QueryWrapper<ImDialog> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(ImDialog::getProjectId, projectId)
                .eq(ImDialog::getUniqueCode, code);
        return count(queryWrapper) > 0;
    }
}
